<template>
  <div class="writeContent">
    <el-card>
      <el-page-header @back="router.back()">
        <template #content>
          <span class="text-large font-600 mr-3" style="color: #fff;"> {{ route.query.title }} </span>
        </template>
      </el-page-header>
    </el-card>
    <div class="writeMain">
      <el-card style="background: black;">
        <div>
          <el-button type="danger" style="width: 100px;height: 45px;margin-bottom: 20px;" @click="cancel">清空</el-button>
          <el-button type="warning" style="width: 150px;height: 45px;margin-bottom: 20px;" @click="save">保存为png</el-button>
        </div>
        <canvas ref="canvasRef" style="border: 8px solid pink;border-radius: 12px;"></canvas>
      </el-card>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, nextTick, watch, onUnmounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'

const route = useRoute()
const router = useRouter()

const write = ref<string>('')

const canvas = ref<any>('')

interface IProps {
  /**
   * @description   画布宽度
   * @default       900
   */
  width?: number;
  /**
   * @description   画布高度
   * @default       450
   */
  height?: number;
  /**
   * @description   线宽
   * @default       4
  */
  lineWidth?: number;
  /**
   * @description   线段颜色
   * @default       'red'
  */
  strokeColor?: string;
  /**
   * @description   设置线条两端圆角
   * @default       'round'
  */
  lineCap?: string;
  /**
   * @description   线条交汇处圆角
   * @default       'round'
  */
  lineJoin?: string;
  /**
   * @description   画布背景颜色
   * @default       'transparent'
  */
  bgColor?: string;
  /**
   * @description   true
  */
  showBtn?: boolean;
  /**
  * @description   当保存时的回调, blob为生成的图片bob
  * @default       -
  */
  onSave?: (blob: Blob) => void;
  /**
   * @description   当画布清空时的回调, 参数为画布的上下文对象,可以直接使用canvas的api
   * @default       -
   */
  onClear?: (canvasContext: CanvasRenderingContext2D) => void;
  /**
  * @description   当画布结束时的回调
  * @default       -
  */
  onDrawEnd?: (canvas: HTMLCanvasElement) => void;
}

const props = withDefaults(defineProps<IProps>(), {
  width: 900,
  height: 450,
  lineWidth: 4,
  strokeColor: '#f8d447',
  lineCap: 'round',
  lineJoin: 'round',
  bgColor: '#364276',
  showBtn: true
});

const {
  width,
  height,
  lineWidth,
  strokeColor,
  lineCap,
  lineJoin,
  bgColor,
  showBtn,
  onSave,
  onClear,
  onDrawEnd
} = props;

const canvasRef = ref<any>(null);
const ctxRef = ref<any>(null);

// 保存上次绘制的 坐标及偏移量
const client = ref<any>({
  offsetX: 0, // 偏移量
  offsetY: 0,
  endX: 0, // 坐标
  endY: 0
})

// 判断是否为移动端
const mobileStatus = (/Mobile|Android|iPhone/i.test(navigator.userAgent));

// 取消-清空画布
const cancel = () => {
  // 清空当前画布上的所有绘制内容
  if (canvasRef.value) {
    const canvasCtx = canvasRef.value.getContext("2d")
    canvasCtx.clearRect(0, 0, width, height)
    onClear && onClear(canvasRef.value)
    
    initCanvas()
  }
}

// 保存-将画布内容保存为图片
const save = () => {
  ElMessageBox.confirm('确定要保存下来吗？', '提示', {
    type: 'warning',
    confirmButtonText: '确定',
    showClose: false,
    closeOnClickModal: false,
  })
    .then(() => {
    // 将canvas上的内容转成blob流
    canvasRef.value.toBlob((blob: any) => {
      // 获取当前时间并转成字符串，用来当做文件名
      const date = Date.now().toString()
      // 创建一个 a 标签
      const a = document.createElement('a')
      // 设置 a 标签的下载文件名
      a.download = `手写板${date}.png`
      // 设置 a 标签的跳转路径为 文件流地址
      a.href = URL.createObjectURL(blob)
      // 手动触发 a 标签的点击事件
      a.click()
      // 移除 a 标签
      a.remove()
      onSave && onSave(blob)
    })
  })
}

// 绘制
const draw = (event: { changedTouches?: any; pageX?: any; pageY?: any; }) => {
  // 获取当前坐标点位
  const { pageX, pageY } = mobileStatus ? event.changedTouches[0] : event
  // 获取canvas 实例
  const canvas: HTMLCanvasElement = canvasRef.value as any;

  const { x, y } = canvas.getBoundingClientRect();
  // 修改最后一次绘制的坐标点
  client.value.endX = pageX
  client.value.endY = pageY
  // 根据坐标点位移动添加线条
  ctxRef.value.lineTo(pageX - x, pageY - y)
  // 绘制
  ctxRef.value.stroke()
}

// 初始化
const init = (event: { changedTouches?: any; offsetX?: any; offsetY?: any; pageX?: any; pageY?: any; }) => {
  // 获取偏移量及坐标
  const { offsetX, offsetY, pageX, pageY } = mobileStatus ? event.changedTouches[0] : event
  const canvas: HTMLCanvasElement = canvasRef.value as any

  const { x, y } = canvas.getBoundingClientRect()

  client.value.offsetX = offsetX
  client.value.offsetY = offsetY
  client.value.endX = pageX
  client.value.endY = pageY

  // 清除以上一次 beginPath 之后的所有路径，进行绘制
  ctxRef.value.beginPath()
  // 根据配置文件设置相应配置
  ctxRef.value.lineWidth = lineWidth
  ctxRef.value.strokeStyle = strokeColor
  ctxRef.value.lineCap = lineCap
  ctxRef.value.lineJoin = lineJoin
  // 设置画线起始点位
  ctxRef.value.moveTo(client.value.endX - x, client.value.endY - y)
  // 监听 鼠标移动或手势移动
  window.addEventListener(mobileStatus ? "touchmove" : "mousemove", draw)
}

// 结束绘制
const closeDraw = () => {
  console.log(ctxRef.value)
  // 结束绘制
  ctxRef.value.closePath()
  // 移除鼠标移动或手势移动监听器
  window.removeEventListener("mousemove", draw)
  onDrawEnd && onDrawEnd(canvasRef.current)
}

const initCanvas = () => {
  // 获取canvas 实例
  const canvas: HTMLCanvasElement = canvasRef.value as any
  // 设置宽高
  canvas.width = width;
  canvas.height = height;
  // 创建上下文
  const ctx: any = canvas.getContext('2d')
  ctxRef.value = ctx;
  // 设置填充背景色
  ctxRef.value.fillStyle = bgColor
  // 绘制填充矩形
  ctxRef.value.fillRect(
    0, // x 轴起始绘制位置
    0, // y 轴起始绘制位置
    width, // 宽度
    height // 高度
  )
}

const addEventListener = () => {
  // 创建鼠标/手势按下监听器
  window.addEventListener(mobileStatus ? "touchstart" : "mousedown", init)
  // 创建鼠标/手势 弹起/离开 监听器
  window.addEventListener(mobileStatus ? "touchend" : "mouseup", closeDraw)
}

const removeEventListener = () => {
  // 创建鼠标/手势按下监听器
  window.removeEventListener(mobileStatus ? "touchstart" : "mousedown", init)
  // 创建鼠标/手势 弹起/离开 监听器
  window.removeEventListener(mobileStatus ? "touchend" : "mouseup", closeDraw)
}

const initEsign = () => {
  initCanvas()
  addEventListener()
}

onMounted(() => {
  initEsign()
})

onUnmounted(() => {
  removeEventListener()
})

</script>

<style lang="scss" scoped>
.writeContent {
  width: 100%;

  .el-card {
    border-radius: $base-card-border-radius;
  }

  .writeMain {
    margin-top: 20px;
    padding: 20px;

    .el-card {
      border-radius: 16px;
      box-shadow: 0px 4px 18px 0px rgba(248, 216, 82, 0.9);
    }
  }

}
</style>